#include <stdio.h>

#include <QDir>
#include <QFile>
#include <QPluginLoader>
#include <QtGui>
#include <QHttp>

#include "mainwindow.h"
#include "ZGLWidget.h"
#include "downloadpdb.h"
#include "data/fileplugin.h"

#include "datamanager/fileextensionmanager.h"
#include "datamanager/datamanagerview.h"
#include "datamanager/geometrydatanode.h"
#include "datamanager/reg3volumedatanode.h"
#include "datamanager/proteindatanode.h"
#include "renderer/rendermanager.h"
#include "renderer/environmentdialog.h"
#include "renderer/openglrenderer.h"
#include "renderer/rendermanagerview.h"
#include "toolplugins/toolplugin.h"

namespace PBVP {
	MainWindow::MainWindow()
	{
		//m_pDataManager = new DataManager();
		//m_pRenderManager = new RenderManager();
		//m_pFileExtManager = new FileExtensionManager();
		m_pMainInterface = new MainInterface(this);
		m_pDataManager = m_pMainInterface->getDataManager();
		m_pRenderManager = m_pMainInterface->getRenderManager();
		m_pFileExtManager = m_pMainInterface->getFileExtensionManager();

		zGLWidget = new ZGLWidget(m_pMainInterface, this);
		zGLWidget->setFocusPolicy(Qt::ClickFocus);
		zGLWidget->updateGL();

		resize(800, 600);
		setCentralWidget(zGLWidget);
		connect(m_pDataManager, SIGNAL(datasetChanged()), zGLWidget, SLOT(updateGL()));
		connect(m_pDataManager, SIGNAL(rendererChanged()), zGLWidget, SLOT(updateGL()));
		// connect(this, SIGNAL(destroyed()), this, SLOT(terminate()));

		createActions();
		createMenus();
		createToolBars();
		createStatusBar();
		loadPlugins();

		createDockWindows();

		setWindowTitle(tr("PBVP"));
		setWindowIcon(QIcon("logo.png"));

		viewMenu->addSeparator();
		viewMenu->addAction(showBBoxAction);
		viewMenu->addAction(environmentAction);

		http = new QHttp(this);
		connect(http, SIGNAL(done(bool)), this, SLOT(downloadPDBFinished(bool)));
	}

	MainWindow::~MainWindow()
	{
		delete m_pMainInterface;
		//delete m_pDataManager;
		//delete m_pFileExtManager;
		//delete m_pRenderManager;
	}

	void MainWindow::createActions()
	{
		fileOpenAction = new QAction(tr("&Open..."), this);
		fileOpenAction->setShortcut(tr("Ctrl+O"));
		fileOpenAction->setStatusTip(tr("Open an existing file"));
		fileOpenAction->setIcon(QIcon("images/open16.png"));
		connect(fileOpenAction, SIGNAL(triggered()), this, SLOT(open()));

		exitAction = new QAction(tr("&Exit"), this);
		connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));

		fullScreenAction = new QAction(tr("Full screen"), this);
		fullScreenAction->setShortcut(tr("Ctrl+F"));
		connect(fullScreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));

		environmentAction = new QAction(tr("Global Options"), this);
		connect(environmentAction, SIGNAL(triggered()), this, SLOT(editEnvironment()));

		aboutAction = new QAction(tr("About"), this);
		aboutAction->setIcon(QIcon("images/about16.png"));
		connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));

		imageSaveAction = new QAction(tr("Save Image"), this);
		imageSaveAction->setIcon(QIcon("images/save16.png"));
		imageSaveAction->setStatusTip(tr("Save the current view as an image file"));
		connect(imageSaveAction, SIGNAL(triggered()), this, SLOT(saveImage()));

		showBBoxAction = new QAction(tr("Show BoundingBox"), this);
		showBBoxAction->setCheckable(true);
		showBBoxAction->setChecked(true);
		connect(showBBoxAction, SIGNAL(toggled(bool)), zGLWidget, SLOT(showBBox(bool)));

		downloadPDBAction = new QAction(tr("Download PDB File"), this);
		downloadPDBAction->setStatusTip(tr("Download a Protein from PDB.org"));
		connect(downloadPDBAction, SIGNAL(triggered()), this, SLOT(downloadPDB()));
	}

	void MainWindow::createMenus()
	{
		fileMenu = menuBar()->addMenu(tr("&File"));
		fileMenu->addAction(fileOpenAction);
		fileMenu->addAction(downloadPDBAction);
		fileMenu->addAction(imageSaveAction);
		fileMenu->addSeparator();
		fileMenu->addAction(exitAction);

		editMenu = menuBar()->addMenu(tr("&Edit"));

		viewMenu = menuBar()->addMenu(tr("&View"));
		viewMenu->addAction(fullScreenAction);

		pluginMenu = menuBar()->addMenu(tr("&Tools"));

		helpMenu = menuBar()->addMenu(tr("&Help"));
		helpMenu->addAction(aboutAction);
	}

	void MainWindow::createToolBars()
	{
		fileToolBar = addToolBar(tr("File"));
		fileToolBar->setIconSize(QSize(16,16));
		fileToolBar->addAction(fileOpenAction);
		fileToolBar->addAction(imageSaveAction);

		helpToolBar = addToolBar(tr("Help"));
		helpToolBar->setIconSize(QSize(16,16));
		helpToolBar->addAction(aboutAction);
	}

	void MainWindow::createStatusBar()
	{
		statusBar()->showMessage(tr("Ready"));
	}

	void MainWindow::loadPlugins()
	{
		QDir pluginsDir = QDir(qApp->applicationDirPath());
		if (pluginsDir.dirName().toLower() == "debug"
			|| pluginsDir.dirName().toLower() == "release") {
			pluginsDir.cdUp();
		}
		if (!pluginsDir.cd("plugins")) {
			// check additional potential plugin path if necessary
			pluginsDir.cdUp();
			pluginsDir.cd("plugins");
		}
		QStringList pluginEntries = pluginsDir.entryList(QDir::Files);

		// add default OpenGL renderer to RenderManager
		OpenGLRenderer *pOGLRenderer = new OpenGLRenderer();
		pOGLRenderer->initialize(m_pMainInterface);
		m_pRenderManager->addRenderPlugin(pOGLRenderer);
		// load plugins from files
		foreach (QString fileName, pluginEntries) {
			QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
			QObject *plugin = loader.instance();
			if (plugin) {
				printf("Loading plugin %s\n", fileName.toStdString().c_str());
				FilePluginInterface *pFilePlugin = qobject_cast<FilePluginInterface *>(plugin);
				if (pFilePlugin) {
					pFilePlugin->initialize(m_pMainInterface);
					m_pFileExtManager->registerPlugin(pFilePlugin);
					// QAction *pluginAction = new QAction(pFilePlugin->description(), this);
					// pluginMenu->addAction(pluginAction);
				}
				RenderPlugin *pRenderPlugin = qobject_cast<RenderPlugin *>(plugin);
				if (pRenderPlugin)
				{
					pRenderPlugin->initialize(m_pMainInterface);
					if (pRenderPlugin->initRender())
					{
						m_pRenderManager->addRenderPlugin(pRenderPlugin);
					}
				}
				ToolPlugin *pToolPlugin = qobject_cast<ToolPlugin *>(plugin);
				if(pToolPlugin)
				{
					pToolPlugin->initialize(m_pMainInterface);
					if(pToolPlugin->getToolMenu()) {
						pluginMenu->addMenu(pToolPlugin->getToolMenu());
					} else if(pToolPlugin->getToolAction()) {
						pluginMenu->addAction(pToolPlugin->getToolAction());
					} // otherwise no entry added to UI
				}
			}
		}
	}

	void MainWindow::createDockWindows()
	{
		propDock = new QDockWidget(tr("Data Properties"), this);
		propDock->setAllowedAreas(Qt::BottomDockWidgetArea);
		propDock->setMinimumHeight(185);
		propDock->setMaximumHeight(400);
		addDockWidget(Qt::BottomDockWidgetArea, propDock);
		viewMenu->addAction(propDock->toggleViewAction());
		propDock->hide();

		QDockWidget *dock = new QDockWidget(tr("Data List"), this);
		dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
		dataList = new DataManagerView(m_pMainInterface, dock);
		dataList->setMaximumWidth(200);
		dock->setWidget(dataList);
		addDockWidget(Qt::RightDockWidgetArea, dock);
		viewMenu->addAction(dock->toggleViewAction());
		connect(dataList, SIGNAL(rendererChanged()), zGLWidget, SLOT(updateGL()));

		dock = new QDockWidget(tr("Renderer Manager"), this);
		dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
		renderView = new RenderManagerView(m_pRenderManager, dock);
		//renderView->setMaximumWidth(200);
		dock->setWidget(renderView);
		addDockWidget(Qt::RightDockWidgetArea, dock);
		viewMenu->addAction(dock->toggleViewAction());
	}

	/************************************************************************/
	/*  QT SLOTS                                                            */
	/************************************************************************/

	void MainWindow::updateView()
	{
		zGLWidget->updateGL();
	}

	void MainWindow::about()
	{
		QMessageBox::information(this, "About PBVP",
			"Version 0.1\ncopyright(c) 2008 Xiaoyu Zhang");
	}
	void MainWindow::open()
	{
		QString fname = QFileDialog::getOpenFileName(this, "Open File", "",
													 m_pFileExtManager->filter());
		if (fname.isNull() ) return;
        FilePluginInterface *pFilePlugin = m_pFileExtManager->getSupportedPlugin(fname);
		if(!pFilePlugin) return;
		DataInterface* pData = pFilePlugin->loadFile(fname);
		switch(pData->getDataSetType()) {
		case GEOMETRY_DATA:
            {
#ifdef _WIN32
            GeometryData *pGeoData = dynamic_cast<GeometryData*>(pData);
#else
            GeometryData* pGeoData = (GeometryData *)(pData);
#endif
			if (pGeoData)
			{
				GeometryDataNode *pGeoNode = new GeometryDataNode(pGeoData, m_pMainInterface);
				pGeoNode->setName(fname);
				m_pDataManager->addDataNode(pGeoNode);
			} }
			break;
		case VOLUME_DATA:
			{
#ifdef _WIN32
				Reg3DVolumeData *pVolData = dynamic_cast<Reg3DVolumeData *>(pData);
#else
				Reg3DVolumeData *pVolData = (Reg3DVolumeData *)(pData);
#endif
				if(pVolData) {
					Reg3DVolumeDataNode* pVolNode = new Reg3DVolumeDataNode(pVolData, m_pMainInterface);
					pVolNode->setName(fname);
					m_pRenderManager->setDefaultRenderer(pVolNode);
					m_pDataManager->addDataNode(pVolNode);
				}
			}
			break;
		case PROTEIN_DATA:
			{
#ifdef _WIN32
				ProteinData *pProtein = dynamic_cast<ProteinData *>(pData);
#else
				ProteinData *pProtein = (ProteinData *)(pData);
#endif
				if(pProtein) {
					ProteinDataNode* pProteinNode = new ProteinDataNode(pProtein, m_pMainInterface);
					pProteinNode->setName(fname);
					m_pRenderManager->setDefaultRenderer(pProteinNode);
					m_pDataManager->addDataNode(pProteinNode);
				}
			}
			break;
		}
	}

	void MainWindow::toggleFullScreen()
	{
		if (zGLWidget->isFullScreen())
		{
			zGLWidget->endFullScreen();
		} else {
			zGLWidget->viewFullScreen();
		}
	}

	void MainWindow::editEnvironment()
	{
		EnvironmentDialog envDialog(m_pRenderManager->environment());
		if (envDialog.exec())
		{
			m_pRenderManager->setEnvironment(envDialog.environment());
			m_pRenderManager->environment().apply();
			zGLWidget->updateGL();
		}
	}

	void MainWindow::saveImage()
	{
		QString fname = QFileDialog::getSaveFileName(this, "Save Image", "", "(*.*)");
		if (!fname.isNull())
		{
			QImage image = zGLWidget->grabFrameBuffer();
			image.save(fname);
		}
	}

	void MainWindow::downloadPDB()
	{
		DownloadPDBDialog downloadDialog(this);
		if(downloadDialog.exec()) {
			//printf("download PDB file\n");
			QString pdbId = downloadDialog.getPDBName();
			QString path = downloadDialog.getSavePath();
			bool isLoad = downloadDialog.isPDBLoad();

			pdbPath = new QFile( path );
			if ( !pdbPath->open( QIODevice::WriteOnly ) )
			{
				QMessageBox::critical( this, tr("Download error"),
					tr("Can't open file '%1' for writing.").arg(path) );
				delete pdbPath;
				return;
			}
			QString pdbURL = "/pdb/cgi/export.cgi/" + pdbId + ".pdb?format=PDB&pdbId=" + pdbId + "&compression=None";

			http->setHost("www.rcsb.org");
			http->get( pdbURL, pdbPath );

		}
	}

	void MainWindow::downloadPDBFinished(bool error)
	{
		if( error )
		{
			QMessageBox::critical( this, tr("Download error"), http->errorString() );
			delete pdbPath; pdbPath = 0;
			return;
		}

		QString pdbPathName = pdbPath->fileName();
		delete pdbPath; pdbPath = 0;

		if( pdbPathName.isEmpty() ) return;

		FilePluginInterface *pFilePlugin = m_pFileExtManager->getSupportedPlugin(pdbPathName);
		if(!pFilePlugin) return;
		DataInterface* pData = pFilePlugin->loadFile(pdbPathName);
#ifdef _WIN32
		ProteinData *pProtein = dynamic_cast<ProteinData *>(pData);
#else
		ProteinData *pProtein = (ProteinData *)(pData);
#endif
		if(pProtein) {
			ProteinDataNode* pProteinNode = new ProteinDataNode(pProtein, m_pMainInterface);
			pProteinNode->setName(pdbPathName);
			m_pRenderManager->setDefaultRenderer(pProteinNode);
			m_pDataManager->addDataNode(pProteinNode);
		}
	}
}
